<?php

namespace App\Admin\Grid;

use App\Admin\Grid\Filter\AbstractFilter;
use ReflectionClass;

/**
 * Class Filter.
 *
 * @method Filter     is($column, $label = '')
 * @method Filter     like($column, $label = '')
 * @method Filter     gt($column, $label = '')
 * @method Filter     lt($column, $label = '')
 * @method Filter     between($column, $label = '')
 * @method Filter     where(\Closure $callback, $label)
 */
class Filter
{
    /**
     * 表格模型
     *
     * @var Model
     */
    protected $model;

    /**
     * @var array
     */
    protected $filters = [];

    /**
     * 支持的内建筛选方法
     *
     * @var array
     */
    protected $supports = ['equal', 'is', 'like', 'gt', 'lt', 'between', 'where'];

    /**
     * 查询表单的action属性
     *
     * @var string
     */
    protected $action;

    /**
     * 查询表单的模板
     *
     * @var string
     */
    protected $view = 'admin.grid.filter';

    /**
     * Filter constructor.
     * @param Model $model
     */
    public function __construct(Model $model)
    {
        $this->model = $model;
    }

    /**
     * 设置查询表单的action属性
     *
     * @param string $action
     *
     * @return $this
     */
    public function setAction($action)
    {
        $this->action = $action;

        return $this;
    }

    /**
     * 获取所有过滤器的条件查询
     *
     * @return array
     */
    public function conditions()
    {
        $inputs = array_dot(\Request::all());

        $inputs = array_filter($inputs, function ($input) {
            return $input !== '' && !is_null($input);
        });

        if (empty($inputs)) {
            return [];
        }

        $params = [];

        foreach ($inputs as $key => $value) {
            array_set($params, $key, $value);
        }

        $conditions = [];

        foreach ($this->filters() as $filter) {
            $conditions[] = $filter->condition($params);
        }

        return array_filter($conditions);
    }

    /**
     * 添加一个过滤
     *
     * @param AbstractFilter $filter
     *
     * @return AbstractFilter
     */
    protected function addFilter(AbstractFilter $filter)
    {
        $filter->setParent($this);

        return $this->filters[] = $filter;
    }

    /**
     * 获取该表格上应用的所有过滤
     *
     * @return AbstractFilter[]
     */
    public function filters()
    {
        return $this->filters;
    }

    /**
     * 把所有过滤器生成的条件语句,应用到模型上
     *
     * @return array
     * @throws \Exception
     */
    public function execute()
    {
        return $this->model->addConditions($this->conditions())->buildData();
    }

    /**
     * 返回过滤查询表单的html
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        if (empty($this->filters)) {
            return '';
        }

        return view($this->view)->with([
            'action'  => $this->action ?: $this->urlWithoutFilters(),
            'filters' => $this->filters,
        ]);
    }

    /**
     * 返回没有任何查询的url,用来重置查询
     *
     * @return string
     */
    protected function urlWithoutFilters()
    {
        $columns = [];

        /**
         * @var Filter\AbstractFilter $filter
         */
        foreach ($this->filters as $filter) {
            $columns[] = $filter->getColumn();
        }

        /**
         * @var \Illuminate\Http\Request $request
         */
        $request = \Request::instance();

        $query = $request->query();
        array_forget($query, $columns);

        $question = $request->getBaseUrl() . $request->getPathInfo() == '/' ? '/?' : '?';

        return rtrim(
            count($request->query()) > 0
                ? $request->url() . $question . http_build_query($query)
                : $request->fullUrl(),
            '?'
        );
    }

    /**
     * 魔术方法调用过滤器
     *
     * @param $method
     * @param $arguments
     * @return AbstractFilter
     * @throws \ReflectionException
     */
    public function __call($method, $arguments)
    {
        if (in_array($method, $this->supports)) {
            $className = '\\App\\Admin\\Grid\\Filter\\' . ucfirst($method);
            $reflection = new ReflectionClass($className);

            return $this->addFilter($reflection->newInstanceArgs($arguments));
        }
    }

    /**
     * @see render()
     */
    public function __toString()
    {
        return $this->render();
    }
}
